package nj.yb.server;

import com.alibaba.fastjson2.JSON;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import io.netty.channel.group.ChannelGroup;
import io.netty.channel.group.DefaultChannelGroup;
import io.netty.util.concurrent.GlobalEventExecutor;
import nj.yb.entity.message.ChatMessage;
import nj.yb.entity.request.HistoryMsgRequest;
import nj.yb.mapper.ChatMessageMapper;
import nj.yb.utils.MyBatisUtils;

import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class HistoryChatServerHandler extends SimpleChannelInboundHandler {

    //用户通道映射
    private static final Map<String,Channel> USER_CHANNEL_MAP = new HashMap<>();
    //所有在线通道（用于群聊）
    private static final ChannelGroup ALL_CHANNELS = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);
    //时间格式化工具
    private SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

    private final ChatMessageMapper chatMessageMapper = MyBatisUtils.getMapper(ChatMessageMapper.class);

    //当前用户名
    private String currentUsername;



    /**
     * 构建系统消息
     * @param content 系统消息内容
     * @return 系统消息的json字符串，包含消息内容和时间
     */
    private String buildSystemMsg(String content){
        Map<String ,Object> msg = new HashMap<>();
        msg.put("type","system");
        msg.put("content",content);
        msg.put("time",sdf.format(new Date()));
        return JSON.toJSONString(msg)+"\n";
    }

    /**
     * 构建用户可见的消息格式
     * @param chatMsg 聊天消息实体
     * @param prefix 前缀
     * @return 消息的json字符串
     */
    private String buildDisplayMsg(ChatMessage chatMsg,String prefix){
        Map<String,Object> msg = new HashMap<>();
        msg.put("type","chat");
        msg.put("prefix",prefix);
        msg.put("content",chatMsg.getContent());
        msg.put("time",sdf.format(chatMsg.getSendTime()));
        return JSON.toJSONString(msg)+"\n";
    }

    /**
     * 添加用户
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        Channel clientChannel = ctx.channel();
        //提示客户端输入用户名
        clientChannel.writeAndFlush(buildSystemMsg("请输入你的用户名："));
        ALL_CHANNELS.add(clientChannel);
    }

    /**
     * 绑定用户名，并拉取信息
     * @param ctx
     * @param username
     */
    private void bindUsername(ChannelHandlerContext ctx,String username){
        Channel clientChannel = ctx.channel();
        if(USER_CHANNEL_MAP.containsKey(username)){
            clientChannel.writeAndFlush(buildSystemMsg("用户名已经被占用，请重新输入："));
            return;
        }

        //绑定用户名
        currentUsername = username;
        USER_CHANNEL_MAP.put(username,clientChannel);
        clientChannel.writeAndFlush(buildSystemMsg("用户名绑定成功！正在拉取历史消息！"));

        //拉取历史消息
        pullHistoryMessage(clientChannel,username,null,100);
        //广播新用户上线
        broadcastExceptSelf(clientChannel,username+"加入聊天");



    }

    /**
     * 广播消息，给除了自己的其他人广播消息
     * @param excludeChannel 排除的用户
     * @param msg 广播消息
     */
    private void broadcastExceptSelf(Channel excludeChannel,String msg){
        for (Channel channel : ALL_CHANNELS) {
            if(channel != excludeChannel){
                channel.writeAndFlush(msg);
            }
        }

    }

    /**
     * 拉取历史消息，并将消息发送给客户端
     * @param clientChannel 客户端通道
     * @param username 用户名
     * @param targetUser 目标用户
     * @param limit 拉取数据条数
     */
    private void pullHistoryMessage(Channel clientChannel,String username ,String targetUser,Integer limit){
        List<ChatMessage> historyMessages;
        if(targetUser != null){
            //拉取与指定用户的私聊历史消息
            historyMessages = chatMessageMapper.queryPrivateHistoryMessages(username,targetUser,limit);
        }else{
            //拉取所有相关历史
            historyMessages = chatMessageMapper.queryHistoryMessages(username,limit);
        }

        //构建历史消息响应
        Map<String ,Object> response = new HashMap<>();
        response.put("type","history");
        response.put("count",historyMessages.size());
        response.put("messages",historyMessages);

        //发送历史消息给客户端
        clientChannel.writeAndFlush(JSON.toJSONString(response)+"\n");
    }



    /**
     * 处理客户端发来的json字符串
     * @param clientChannel 客户端通道
     * @param jsonMsg json字符串
     */
    private void handleJsonMessage(Channel clientChannel,String jsonMsg){
        //尝试解析为历史消息
        try {
            HistoryMsgRequest request = JSON.parseObject(jsonMsg,HistoryMsgRequest.class);
            if(request != null && request.getUsername() != null){
                //拉取历史消息
                pullHistoryMessage(clientChannel,request.getUsername(),request.getTargetUser(),request.getLimit());
                return;
            }
        }catch (Exception e){
            //不是历史消息，尝试解析为聊天消息
        }

        //解析为聊天消息
        ChatMessage chatMsg = JSON.parseObject(jsonMsg,ChatMessage.class);
        if(chatMsg == null || chatMsg.getContent() == null){
            clientChannel.writeAndFlush(buildSystemMsg("消息格式错误，请重新发送！"));
            return;
        }

        //补全消息元数据
        chatMsg.setSender(currentUsername);
        chatMsg.setSendTime(new Date());

        //持久化消息到数据库
        chatMessageMapper.insertMessage(chatMsg);

        //转发消息
        if(ChatMessage.TYPE_PRIVATE.equals(chatMsg.getMsgType()) && chatMsg.getReceiver() != null) {
            sendPrivateMessage(clientChannel,chatMsg);
        }else{
            sendGroupMessage(clientChannel,chatMsg);
        }
    }

    /**
     * 私聊消息转发
     * @param senderChannel 发送者的通道
     * @param chatMsg 聊天消息
     */
    private void sendPrivateMessage(Channel senderChannel,ChatMessage chatMsg){
        String targetUsername = chatMsg.getReceiver();
        Channel targetChannel = USER_CHANNEL_MAP.get(targetUsername);

        //构建发送者和接收这看到的消息
        String senderMsg = buildDisplayMsg(chatMsg, "私聊-发给"+targetUsername);
        String targetMsg = buildDisplayMsg(chatMsg,"私聊-"+currentUsername+"发来");

        //发送消息
        senderChannel.writeAndFlush(senderMsg);
        if(targetChannel != null){
            targetChannel.writeAndFlush(targetMsg);
        }else{
            senderChannel.writeAndFlush(buildSystemMsg("用户 "+ targetUsername + " 不在线，消息已存入历史!"));
        }
    }

    /**
     * 群聊消息转发
     * @param senderChannel
     * @param chatMsg
     * @return
     */
    private void sendGroupMessage(Channel senderChannel,ChatMessage chatMsg){
        String groupMsg = buildDisplayMsg(chatMsg,currentUsername);
        ALL_CHANNELS.writeAndFlush(groupMsg);
    }

    /**
     * 解析为纯文本消息
     * @param clientChannel
     * @param content
     */
    private void handlePlainTextMessage(Channel clientChannel,String content){
        //构建群聊消息
        ChatMessage chatMsg = new ChatMessage();
        chatMsg.setMsgType(ChatMessage.TYPE_GROUP);
        chatMsg.setSender(currentUsername);
        chatMsg.setContent(content);
        chatMsg.setSendTime(new Date());

        //持久化并发送
        chatMessageMapper.insertMessage(chatMsg);
        sendGroupMessage(clientChannel,chatMsg);
    }

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, Object o) throws Exception {
        Channel clientChannel = ctx.channel();
        //未绑定用户名,第一条消息是用户名
        if(currentUsername == null){
            bindUsername(ctx,o.toString().trim());
            return;
        }
        //已经绑定了用户名
        try{
            handleJsonMessage(clientChannel,o.toString());
        }catch (Exception e){
            //兼容旧版本（若用户直接输入文本，按群聊处理）
            handlePlainTextMessage(clientChannel,o.toString());
        }

    }


    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        if(currentUsername != null){
            USER_CHANNEL_MAP.remove(currentUsername);
            broadcastExceptSelf(ctx.channel(),buildSystemMsg(currentUsername+ " 离开聊天"));
        }
        ALL_CHANNELS.remove(ctx.channel());
    }

    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        if(currentUsername != null){
            USER_CHANNEL_MAP.remove(currentUsername);
            broadcastExceptSelf(ctx.channel(),buildSystemMsg(currentUsername+" 离开聊天"));
        }
        ALL_CHANNELS.remove(ctx.channel());
    }
}
